package tarea;

/*
*Universidad del Valle de Guatemala
* Roger Artemio Diaz Fuentes
* 12176
* Juan de Dios Chivalan Rojas
* 12175
* Esta es la implementacion del arbol 2-3
* Aqui se esta haciendo la utilizacion de un codigo encontrado en internet ya que se encontro mucha similitud con 
* los requerimientos de la tarea.
* 
 */

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Point2D.Float;
import java.util.LinkedList;
import java.util.Queue;

/**
 * @author base code by Chip Klostermeyer @
 *         http://www.unf.edu/~wkloster/3540/234tree.java modified by Team C
 *         2-3-4 Node implementation by Team C
 * 
 * @param <AnyType>
 *            type of element for the node
 */
class DataElement234<AnyType> {

	/**
	 * All the possible states of any DataElement234 in a 234Node
	 * 
	 * @author Simeon Gbolo
	 * 
	 */
	enum DataElement234State
	{
		moving, paused
	}

	// the current location on the node in the x,y plane
	private Float currentXYlocation;

	// the current destination of the node in the x,y plane
	private Float currentXYdestination;

	private Float startingPoint;

	// running time for this animation
	float runningTime;

	// used to calculate the slope for moving from one point to another
	float rise;

	// Used to calculate the slope for moving from one spot to another
	float run;

	// used to animate a node when found
	float foundCounter = 0;

	// a path for this node to follow(not implemented)
	Queue<Float> path;

	DataElement234State state;

	// Character width factor
	private static final int CHAR_WIDTH = 8;
	// Character height factor
	private static final int CHAR_HEIGHT = 10;
	// Minimum node element border diameter
	private static final int MIN_DRAW_DIA = 20;

	// Diameter of circular border
	private int drawDiameter;
	private AnyType key;
	private Point center;
	private boolean found;

	public DataElement234(AnyType e)
	{
		key = e;
		found = false;
		drawDiameter = MIN_DRAW_DIA;
		currentXYlocation = new Float();// added simeon
		currentXYdestination = new Float();// added simeon
		startingPoint = new Float();// added simeon
		path = new LinkedList<Float>();// added simeon
		runningTime = 0;// added simeon
		state = DataElement234State.paused;// added simeon
	}

	/**
	 * @return the drawDiameter
	 */
	public int getDrawDiameter()
	{
		return drawDiameter;
	}

	public AnyType getKey()
	{
		return key;
	}

	public boolean isFound()
	{
		return found;
	}

	public void setFound(boolean found)
	{
		this.found = found;
	}

	public Point getCenter()
	{
		return center;
	}

	public void setCenter(Point loc)
	{
		center = loc;
	}

	/**
	 * Shifts the center coordinates of this element
	 * 
	 * @param dx
	 *            - the delta X for the shift
	 * @param dy
	 *            - the delta Y for the shift
	 */
	public void moveCenter(int dx, int dy)
	{
		if (center != null)
		{
			center.translate(dx, dy);
		}
	}

	public String toString()
	{
		return "" + key;
	}

	// public void displayItem() {
	// System.out.print("/" + key);
	// }

	public void drawElement(Graphics2D graph_2d)
	{
		if (center != null)
		{
			// Draw element border
			graph_2d.drawOval(upperLeftX(), upperLeftY(), drawDiameter,
					drawDiameter);
			// Draw node data
			if (key != null)
			{
				graph_2d.drawString(key.toString(), dataCenterX(),
						dataCenterY());
			}
		}
	}

	/**
	 * Calculates the upper-left X-coordinate for element border
	 * 
	 * @return The center X-coordinate less half of border width
	 */
	private int upperLeftX()
	{
		return (int) (center.getX() - (drawDiameter / 2));
	}

	/**
	 * Calculates the upper-left Y-coordinate for element border
	 * 
	 * @return The center Y-coordinate less half of border height
	 */
	private int upperLeftY()
	{
		return (int) (center.getY() - (drawDiameter / 2));
	}

	public int dataCenterX()
	{
		if (key != null && key.toString() != "")
		{
			return (int) (center.getX() - (key.toString().length() * CHAR_WIDTH / 2));
		}

		return (int) center.getX();
	}

	public int dataCenterY()
	{

		return (int) (center.getY() + (CHAR_HEIGHT / 2));
	}

	/**
	 * Calculates the elements destination and sets it up for navigation to the
	 * destination
	 */
	public void calculateElementDestination()
	{
		if (center != null)
		{

			Float xYDest = new Float();

			xYDest.setLocation(upperLeftX(), upperLeftY());
			setUpForNavigation(xYDest);

		}
	}

	/**
	 * This method makes the node update it's self depending on it's current
	 * state
	 * 
	 * @param deltaTime
	 */
	public void advance(float deltaTime)
	{

		if (state == DataElement234State.moving)
		{
			updateMoving(deltaTime);
		}

		if (state == DataElement234State.paused)
		{
			updatePaused(deltaTime);
		}

	}

	/**
	 * @author Simeon Gbolo depending on what ever is first point in the path
	 *         queue, the node will travel to that location. once it reaches
	 *         that location and if the Queue is not empty - it will move on to
	 *         the next point in the queue
	 * 
	 * @param deltaTime
	 *            the change in time
	 */
	private void updateMoving(float deltaTime)
	{
		runningTime += deltaTime;

		float newX = (float) (startingPoint.getX() + run * runningTime);
		float newY = (float) (startingPoint.getY() + rise * runningTime);
		currentXYlocation.setLocation(newX, newY);
		// System.out.println("" +getDistanceFromDestination() );
		if (getDistanceFromDestination() <= 2)
		{
			if (!path.isEmpty())
			{
				setUpForNavigation(path.poll());
			} else
			{

				currentXYlocation.setLocation(currentXYdestination);
				state = DataElement234State.paused;
			}
		}
	}

	/**
	 * @author Simeon gbolo set running time to 0 and if the node is found while
	 *         paused add .5 to node counder until it reaches 1000 - then the
	 *         node will be back it's original color
	 * 
	 * @param deltaTime
	 */
	private void updatePaused(float deltaTime)
	{
		runningTime = 0;
		if (found)
		{
			foundCounter += .5;
		}

		if (foundCounter > 1000)
		{
			found = false;
			foundCounter = 0;
		}

	}

	/**
	 * @author Simeon Gbolo This method is used to calculate the distance from
	 *         one point to another.
	 * @return The distance from one point to another
	 */
	public int getDistanceFromDestination()
	{

		return (int) currentXYlocation.distance(currentXYdestination);
	}

	/**
	 * @author Simeon Gbolo
	 * @return the x current x location of this node
	 */
	public int getX()
	{

		return (int) currentXYlocation.getX();
	}

	/**
	 * @return the currentXYlocation
	 */
	public Float getCurrentXYlocation()
	{
		return currentXYlocation;
	}

	/**
	 * @author Simeon Gbolo
	 * @return the current y location of this node
	 */
	public int getY()
	{

		return (int) currentXYlocation.getY();

	}

	/**
	 * @author Simeon gbolo Used to calculate the slope from one point to
	 *         another This should be called only once before navigating from
	 *         one point to another, This method also sets the new starting
	 *         point for your node's navigation
	 */
	public void getSlope()
	{
		rise = (float) (currentXYdestination.getY() - currentXYlocation.getY());
		run = (float) (currentXYdestination.getX() - currentXYlocation.getX());
		startingPoint.setLocation(currentXYlocation);
	}

	/**
	 * @author Simeon gbolo This method sets up the math needed to navigate from
	 *         one point to another with a given delta time
	 */
	public void setUpForNavigation(Float new_destination_point)
	{
		startingPoint.setLocation(currentXYlocation);
		currentXYdestination.setLocation(new_destination_point);
		getSlope();
		runningTime = 0;
		state = DataElement234State.moving;

	}

	/**
	 * @author simeon gbolo
	 * @param path
	 *            the path to set
	 */
	public void setPath(Queue<Float> path)
	{
		this.path = path;
		if (!this.path.isEmpty())
		{
			setUpForNavigation(this.path.poll());
			state = DataElement234State.moving;
		}
	}
}